package org.activequant.data.retrieval.mock;

import java.util.Random;

import org.activequant.core.domainmodel.InstrumentSpecification;
import org.activequant.core.domainmodel.Symbol;
import org.activequant.core.domainmodel.marketscanner.InstrumentSpecificationDetails;
import org.activequant.core.domainmodel.marketscanner.MarketScanChange;
import org.activequant.core.domainmodel.marketscanner.ScanCriteria;
import org.activequant.core.types.Currency;
import org.activequant.core.types.SecurityType;
import org.activequant.core.types.TimeFrame;
import org.activequant.data.retrieval.IMarketScanChangeSubscriptionSource;
import org.activequant.data.retrieval.IMarketScanSubscription;

/**
 * Mock source of MarketScanChange subscriptions. Use it for unit testing
 * services that depend on subscription service. Supports only one outstanding
 * subscription.
 * <p>
 * To use, instantiate the mock subscription source, then hook it into the
 * application. Then, use {@link #fireEvent} method to simulate incoming
 * MarketScanChange entities. <br>
 * 
 * @author Mike Kroutikov
 * @author Ulrich Staudinger
 */
public class MockMarketScanChangeSubscriptionSource extends MockScanSubscriptionSourceBase<MarketScanChange> implements IMarketScanChangeSubscriptionSource {
	/**
	 * private final static int ROWS_PER_SCAN=50;
	 */
	private final static int ROWS_PER_SCAN=50;
	/**
	 * private final static int ABOVE_PRICE=20;
	 */
	private final static int ABOVE_PRICE=20;
	
	private final static int MARKET_CAP_ABOVE=100000000;
	
	private ScanCriteria HIGH_OPT_VOLUME_PUT_CALL_RATIO=null;
	/**
	 * this means a bearish underlying (investors think it will go down soon)
	 * @return
	 */
	private ScanCriteria getTOP_PERC_GAIN_Criteria(){
		if(HIGH_OPT_VOLUME_PUT_CALL_RATIO==null){
			HIGH_OPT_VOLUME_PUT_CALL_RATIO=new ScanCriteria();
			HIGH_OPT_VOLUME_PUT_CALL_RATIO.setNumberOfRows(ROWS_PER_SCAN);
			HIGH_OPT_VOLUME_PUT_CALL_RATIO.setVendor("IB");
			HIGH_OPT_VOLUME_PUT_CALL_RATIO.setInstrument("STK");
			HIGH_OPT_VOLUME_PUT_CALL_RATIO.setLocationCode("STK.US");
			HIGH_OPT_VOLUME_PUT_CALL_RATIO.setScanCode("TOP_PERC_GAIN");
			HIGH_OPT_VOLUME_PUT_CALL_RATIO.setAbovePrice(ABOVE_PRICE);
			HIGH_OPT_VOLUME_PUT_CALL_RATIO.setBelowPrice(ScanCriteria.NOT_SET);
			HIGH_OPT_VOLUME_PUT_CALL_RATIO.setAboveVolume(0);
			HIGH_OPT_VOLUME_PUT_CALL_RATIO.setAverageOptionVolumeAbove(0);
			HIGH_OPT_VOLUME_PUT_CALL_RATIO.setMarketCapAbove(MARKET_CAP_ABOVE);
			HIGH_OPT_VOLUME_PUT_CALL_RATIO.setMarketCapBelow(ScanCriteria.NOT_SET);
			HIGH_OPT_VOLUME_PUT_CALL_RATIO.setMoodyRatingAbove("");
			HIGH_OPT_VOLUME_PUT_CALL_RATIO.setMoodyRatingBelow("");
			HIGH_OPT_VOLUME_PUT_CALL_RATIO.setSpRatingAbove("");
			HIGH_OPT_VOLUME_PUT_CALL_RATIO.setSpRatingBelow("");
			HIGH_OPT_VOLUME_PUT_CALL_RATIO.setMaturityDateAbove(null);
			HIGH_OPT_VOLUME_PUT_CALL_RATIO.setMaturityDateBelow(null);
			HIGH_OPT_VOLUME_PUT_CALL_RATIO.setCouponRateAbove(ScanCriteria.NOT_SET);
			HIGH_OPT_VOLUME_PUT_CALL_RATIO.setCouponRateBelow(ScanCriteria.NOT_SET);
			HIGH_OPT_VOLUME_PUT_CALL_RATIO.setExcludeConvertible("0");
			HIGH_OPT_VOLUME_PUT_CALL_RATIO.setScannerSettingPairs("Annual,true");
			HIGH_OPT_VOLUME_PUT_CALL_RATIO.setStockTypeFilter("ALL");
		}
		return HIGH_OPT_VOLUME_PUT_CALL_RATIO;
	}
	
	private ScanCriteria LOW_OPT_VOLUME_PUT_CALL_RATIO=null;
	/**
	 * this means a bullish underlying (investors think it will go up soon)
	 * @return
	 */
	private ScanCriteria getLOW_OPT_VOLUME_PUT_CALL_RATIO_Criteria(){
		if(LOW_OPT_VOLUME_PUT_CALL_RATIO==null){
			LOW_OPT_VOLUME_PUT_CALL_RATIO=new ScanCriteria();
			LOW_OPT_VOLUME_PUT_CALL_RATIO.setNumberOfRows(ROWS_PER_SCAN);
			LOW_OPT_VOLUME_PUT_CALL_RATIO.setVendor("IB");
			LOW_OPT_VOLUME_PUT_CALL_RATIO.setInstrument("STK");
			LOW_OPT_VOLUME_PUT_CALL_RATIO.setLocationCode("STK.US");
			LOW_OPT_VOLUME_PUT_CALL_RATIO.setScanCode("LOW_OPT_VOLUME_PUT_CALL_RATIO");
			LOW_OPT_VOLUME_PUT_CALL_RATIO.setAbovePrice(ABOVE_PRICE);
			LOW_OPT_VOLUME_PUT_CALL_RATIO.setBelowPrice(ScanCriteria.NOT_SET);
			LOW_OPT_VOLUME_PUT_CALL_RATIO.setAboveVolume(0);
			LOW_OPT_VOLUME_PUT_CALL_RATIO.setAverageOptionVolumeAbove(0);
			LOW_OPT_VOLUME_PUT_CALL_RATIO.setMarketCapAbove(MARKET_CAP_ABOVE);
			LOW_OPT_VOLUME_PUT_CALL_RATIO.setMarketCapBelow(ScanCriteria.NOT_SET);
			LOW_OPT_VOLUME_PUT_CALL_RATIO.setMoodyRatingAbove("");
			LOW_OPT_VOLUME_PUT_CALL_RATIO.setMoodyRatingBelow("");
			LOW_OPT_VOLUME_PUT_CALL_RATIO.setSpRatingAbove("");
			LOW_OPT_VOLUME_PUT_CALL_RATIO.setSpRatingBelow("");
			LOW_OPT_VOLUME_PUT_CALL_RATIO.setMaturityDateAbove(null);
			LOW_OPT_VOLUME_PUT_CALL_RATIO.setMaturityDateBelow(null);
			LOW_OPT_VOLUME_PUT_CALL_RATIO.setCouponRateAbove(ScanCriteria.NOT_SET);
			LOW_OPT_VOLUME_PUT_CALL_RATIO.setCouponRateBelow(ScanCriteria.NOT_SET);
			LOW_OPT_VOLUME_PUT_CALL_RATIO.setExcludeConvertible("0");
			LOW_OPT_VOLUME_PUT_CALL_RATIO.setScannerSettingPairs("Annual,true");
			LOW_OPT_VOLUME_PUT_CALL_RATIO.setStockTypeFilter("ALL");
		}
		return LOW_OPT_VOLUME_PUT_CALL_RATIO;
	}
	private boolean runFlag = false;
	private int randomDelayMax = 1000;
	private String[] symbols = { "MSFT", "NVDA", "GOOG", "AAPL", "CSCO", "INTC", "RIMM" };
	private Random generator = new Random();

	public IMarketScanSubscription<MarketScanChange> subscribe(ScanCriteria spec) throws Exception {
		return super.subscribe(spec, TimeFrame.TIMEFRAME_1_TICK);
	}

	private class MarketScanChangeEmitter implements Runnable {
		ScanCriteria s;

		MarketScanChangeEmitter(ScanCriteria s) {
			this.s = s;
		}

		public void run() {
			while (runFlag) {
				try {

					MarketScanChange change = new MarketScanChange();
					InstrumentSpecificationDetails instrumentSpecificationDetails = new InstrumentSpecificationDetails();
					InstrumentSpecification instrumentSpecification = new InstrumentSpecification();
					int symbolIndex = generator.nextInt(symbols.length);
					String symbol = symbols[symbolIndex];
					instrumentSpecification.setSymbol(new Symbol(symbol));
					instrumentSpecification.setExchange("SMART");
					instrumentSpecification.setVendor("IB");
					instrumentSpecification.setCurrency(Currency.USD);
					instrumentSpecification.setSecurityType(SecurityType.STOCK);
					instrumentSpecificationDetails.setInstrumentSpecification(instrumentSpecification);
					change.setInstrumentSpecificationDetails(instrumentSpecificationDetails);
					change.setScanCriteria(getTOP_PERC_GAIN_Criteria());
					Thread.sleep((int) (Math.random() * randomDelayMax));
					fireEvent(change);
				} catch (Exception x) {
					log.warn("Exception while emitting marketScanchange", x);
				}
			}
		}

	}

	public void startRandomEmission() {
		runFlag = true;
		// over all the subscriptions.
		IMarketScanSubscription<MarketScanChange>[] subs = this.getSubscriptions();
		for (IMarketScanSubscription<MarketScanChange> s : subs) {
			Thread t = new Thread(new MarketScanChangeEmitter(s.getScanCriteria()));
			t.start();
		}
	}

	public void stopRandomEmission() {
		runFlag = false;
	}

}
